#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

void procc(void)
{
    struct ifreq ifr = {0};
    struct sockaddr_can can_addr = {0};
    struct can_frame frame = {0};
    int sockfd = -1;
    int i, ret;

    //打开套接字
    sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (0 > sockfd)
    {
	    perror("socket error!");
	    exit(EXIT_FAILURE);
    }

    //指定can0设备
    strcpy(ifr.ifr_name, "can0");
    ioctl(sockfd, SIOCGIFINDEX, &ifr);
    can_addr.can_family = AF_CAN;
    can_addr.can_ifindex = ifr.ifr_ifindex;

    //将can0与套接字进行绑定
    ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
    if(0 > ret)
    {
        perror("bind error!");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    //设置过滤规则
    //setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
    //接收数据

    while(1)
    {
        if(0 > read(sockfd, &frame, sizeof(struct can_frame)))
        {
            perror("water read error!");
            break;
        }

        // 校验是否接收到错误帧
        if(frame.can_id & CAN_ERR_FLAG)
        {
            printf("water Error frame!\n");
            break;
        }

        //检验帧格式： 标准帧还是扩展帧
        if (frame.can_id & CAN_EFF_FLAG)
            printf("扩展帧 <0x%08x> ", frame.can_id & CAN_EFF_MASK);
        else
            printf("标准帧 <0x%03x> ", frame.can_id & CAN_SFF_MASK);

        //校验帧类型： 数据帧还是远程帧
        if (frame.can_id & CAN_RTR_FLAG)
        {
            printf(" remote request!\n");
            continue;
        }

        //打印数据长度
        printf(" [%d] ", frame.can_dlc);

        //打印数据
        for(i = 0; i < frame.can_dlc; i++)
            printf(" %02x ", frame.data[i]);

        printf(" [子进程 接收数据] \n");

    }

    //关闭套接字
    close(sockfd);
    exit(EXIT_SUCCESS);
}

int main(void)
{
    struct ifreq ifr = {0};
    struct sockaddr_can can_addr = {0};
    struct can_frame frame = {0};
    int sockfd = -1;
    int ret, status, i;

    switch (fork())
    {
        case -1:
            perror("fork error!");
            exit(-1);
        case 0:
        //子进程
            procc();
            sleep(1);
            _exit(0);
        default:
            //父进程
            break;
    }

    //打开套接字
    sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if(0 > sockfd)
    {
        perror("socket error!");
        exit(EXIT_FAILURE);
    }

    //指定 can0 设备
    strcpy(ifr.ifr_name, "can0");
    ioctl(sockfd, SIOCGIFINDEX, &ifr);
    can_addr.can_family = AF_CAN;
    can_addr.can_ifindex = ifr.ifr_ifindex;

    //将 can0 与套接字进行绑定
    ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
    if(0 > ret)
    {
        perror("bind error!");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

//设置过滤规则， 不接收任何报文，仅发送数据
    setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

    //发送数据
    frame.data[0] = 0xA0;
    frame.data[1] = 0xB0;
    frame.data[2] = 0xC0;
    frame.data[3] = 0xD0;
    frame.data[4] = 0xE0;
    frame.data[5] = 0x11;
    frame.data[6] = 0x22;
    frame.data[7] = 0x33;
    frame.can_dlc = 8;
    frame.can_id = 0x321;

    while(1)
    {
        ret = write(sockfd, &frame, sizeof(frame));
        if(sizeof(frame) != ret)
        {
            perror("write error!");
            goto TT;
        }

        printf("[OnefU] 主进程发送数据成功！\n");
        sleep(1);
    }

    ret = waitpid(-1, &status, 0);
    if(-1 == ret)
    {
        if (ECHILD == errno)
        {
            printf("没有需要等待回收的子进程！\n");
            exit(0);
        }
        else
        {
            perror("wait error");
            exit(-1);
        }
    }

    printf("回收子进程<%d>, 终止状态<%d>\n", ret, WEXITSTATUS(status));

TT:
//关闭套接字
    close(sockfd);
    exit(EXIT_SUCCESS);



	//printf("hello world!\n");
}
